import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.text.AbstractWriter;
import java.lang.Math;

import datastructures.Cache;
import datastructures.Instruction;
import datastructures.MemoryEntry;
import datastructures.ROBEntry;
import datastructures.Register;
import datastructures.Reservation_Station_Entry;


public class Simulator {
	
	static ArrayList<Cache> caches = new ArrayList<Cache>();
	ArrayList<Object[]> mem = new ArrayList<Object[]>();
	static Object [][] memory = new Object[100][5];
	//static Hashtable<String, Object[]> mem = new Hashtable<String, Object[]>();
	static Register [] R;
	int ways;
	int firstInstructionAddress;
	int bufferSize;
	int addUnits;
	int mulUnits;
	int divUnits;
	int addCycles;
	int mulCycles;
	int divCycles;
	int robIndex;
	int robSize;
	int memoryTagIndex;
	int PC;
	int time = -1;
	ArrayList<Instruction> instructionsSet = new ArrayList<Instruction>();
	ArrayList<ROBEntry> rob = new ArrayList<ROBEntry>();
	Reservation_Station_Entry[] addReservationStation;
	Reservation_Station_Entry[] multiplyReservationStation;
	Reservation_Station_Entry[] divideReservationStation;
	Hashtable<Integer, Integer> registerMap = new Hashtable<Integer, Integer>();
	
	boolean branchPrediction = false;
	int branchTaken = -1;
	
	
	public Simulator(){
		R = new Register[8];
		for(int i = 0; i < R.length; i++){
			R[i] = new Register(""+i);
			R[i].setData(i);
		}
	}
	
	
	
	
	
	public void simulate(){
		
		System.out.println("Simulation Started.");
		addInstructionsToMemory();
		initializeCaches();
		initializeROB();
		initializeReservationStations();
		
		PC = firstInstructionAddress;
		
		ArrayList<Instruction> loadedInstructions = new ArrayList<Instruction>();
		ArrayList<Instruction> branchPredictionInstructions = new ArrayList<Instruction>();
		
		for(int i = 0; (i < ways) && (PC < firstInstructionAddress + instructionsSet.size()); i++){
			
			if(branchPrediction)
				branchPredictionInstructions.add((Instruction)((MemoryEntry)memory[PC][0]).getData());
			else
				loadedInstructions.add((Instruction)((MemoryEntry)memory[PC][0]).getData());
			
			
			if (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType().equalsIgnoreCase("jmp")
					|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
							.equalsIgnoreCase("beq"))
					|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
							.equalsIgnoreCase("jalr"))
					|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
							.equalsIgnoreCase("ret"))){
				
				branchPrediction = true;
				
			}
			
			PC++;
			
		}
		
		while(! finishedSimulation(loadedInstructions)){
			
			time++;
			System.out.println("at clock cycle t" + time);
			
			if(time > 0)
				for(int i = 0; (i < ways) && (PC < firstInstructionAddress + instructionsSet.size()); i++){
					
					if(branchPrediction)
						branchPredictionInstructions.add((Instruction)((MemoryEntry)memory[PC][0]).getData());
					else
						loadedInstructions.add((Instruction)((MemoryEntry)memory[PC][0]).getData());

					
					if (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType().equalsIgnoreCase("jmp")
							|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
									.equalsIgnoreCase("beq"))
							|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
									.equalsIgnoreCase("jalr"))
							|| (((Instruction)((MemoryEntry)memory[PC][0]).getData()).getType()
									.equalsIgnoreCase("ret"))){
						
						branchPrediction = true;
						
					}
					
					PC++;
					
				}
			
			// instructions in branchPredictioInstructions never commit.
			for(int i = 0; i < branchPredictionInstructions.size(); i++){
				
				Instruction inst = branchPredictionInstructions.get(i);
				if(inst.getFinishedStages() == 0)
					decode(inst);
				else{
					if(inst.getFinishedStages() == 1)
						dispatch(inst);
					else{
						if(inst.getFinishedStages() == 2)
							issue(inst);
						else
							if(inst.getFinishedStages() == 3)
								execute(inst);
						
						}
					}
				
			}
			
			
			for(int i = 0; i < loadedInstructions.size(); i++){
				
				if(i < (time +1 ) * ways)
					if(loadedInstructions.get(i).getFinishedStages() != 5){
						
						callAppropriateStage(loadedInstructions.get(i));
						
						if(branchTaken == 1){ // if the branch was taken
							
							branchPredictionInstructions = new ArrayList<Instruction>();
							branchTaken = -1;
							
						}else{
							
							if(branchTaken == 0){ // if the branch was not taken
								
								for(int j = 0; j < branchPredictionInstructions.size(); j++)
									loadedInstructions.add(branchPredictionInstructions.get(j));
								
								branchPredictionInstructions = new ArrayList<Instruction>();
								branchTaken = -1;
								
							}
							
						}
						
					}
				
			}
			
		}
		
		
	}
	
	
	
	public boolean finishedSimulation(ArrayList<Instruction> instructions){
		
		for(int i = 0; i < instructions.size(); i++)
			if(instructions.get(i).getFinishedStages() != 5)
				return(false);
		
		return(true);
		
	}
	
	
	public void callAppropriateStage(Instruction instruction){
		
		if(instruction.getFinishedStages() == 0)
			decode(instruction);
		else{
			if(instruction.getFinishedStages() == 1)
				dispatch(instruction);
			else{
				if(instruction.getFinishedStages() == 2)
					issue(instruction);
				else{
					if(instruction.getFinishedStages() == 3)
						execute(instruction);
					else
						if(instruction.getFinishedStages() == 4)
							commit(instruction);
					}
				}
			}
		
	}
	
	
	
	public void initializeReservationStations(){
		
		for(int i = 0; i < addReservationStation.length; i++)
			addReservationStation[i] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		for(int i = 0; i < multiplyReservationStation.length; i++)
			multiplyReservationStation[i] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		for(int i = 0; i < divideReservationStation.length; i++)
			divideReservationStation[i] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		
	}
	
	
	public void initializeROB(){
		
		for(int i = 0; i < robSize; i++)
			rob.add(new ROBEntry(false, -1, -1));
		
	}
	
	
	public void addInstruction(Instruction instruction){
		
		if(instruction.getType().equalsIgnoreCase("add")
				|| instruction.getType().equalsIgnoreCase("addi")
				|| instruction.getType().equalsIgnoreCase("lw")
				|| instruction.getType().equalsIgnoreCase("sw")
				|| instruction.getType().equalsIgnoreCase("jmp")
				|| instruction.getType().equalsIgnoreCase("beq")
				|| instruction.getType().equalsIgnoreCase("jalr")
				|| instruction.getType().equalsIgnoreCase("ret")
				|| instruction.getType().equalsIgnoreCase("nand"))
			instruction.setExecutionTime(addCycles);
		if(instruction.getType().equalsIgnoreCase("mul"))
			instruction.setExecutionTime(mulCycles);
		if(instruction.getType().equalsIgnoreCase("div"))
			instruction.setExecutionTime(divCycles);
		
		this.instructionsSet.add(instruction);
		
	}
	
	public void addInstructionsToMemory(){
		
		int memoryIndex = firstInstructionAddress;
//		Object[] lineTemp = new Object[1];
		for(int i = 0; i < instructionsSet.size(); i++){
			
			MemoryEntry memoryEntry = new MemoryEntry(memoryTagIndex, instructionsSet.get(i));
			memoryTagIndex++;
			memory[memoryIndex] = new Object[] {memoryEntry};
			memoryIndex++;

		}
	}
	
	public void initializeCaches(){
		for(int i = caches.size()-1; i >= 0;i--){
			for(int j = 0;j < caches.get(i).getC();j++){
				caches.get(i).cache[j] = memory[j];
			}
		}
	}
	
	public void divertToInstructionType(Instruction instruction){
		if(instruction.getType().equalsIgnoreCase("ADD"))
			ADD(instruction);
		else if(instruction.getType().equalsIgnoreCase("ADDI"))
			ADDI(instruction);
		else if(instruction.getType().equalsIgnoreCase("MUL"))
			MUL(instruction);
		else if(instruction.getType().equalsIgnoreCase("DIV"))
			DIV(instruction);
		else if(instruction.getType().equalsIgnoreCase("RET"))
			RET(instruction);
		else if(instruction.getType().equalsIgnoreCase("JALR"))
			JALR(instruction);
		else if(instruction.getType().equalsIgnoreCase("BEQ"))
			BEQ(instruction);
		else if(instruction.getType().equalsIgnoreCase("JMP"))
			JMP(instruction);
		else if(instruction.getType().equalsIgnoreCase("SW"))
			SW(instruction);
		else if(instruction.getType().equalsIgnoreCase("LW"))
			LD(instruction);
		else if(instruction.getType().equalsIgnoreCase("nand"))
			NAND(instruction);
	}
	
	public void SW(Instruction instruction){
		
		memory[instruction.getOp2()][instruction.getImmediate()] =  new MemoryEntry(memoryTagIndex, R[instruction.getOp1()].getData());
		memoryTagIndex++;
//		Object[] temp = memory[instruction.getImmediate() + instruction.getOp2()];
//		int tempTag = 0;/////////////////////////////////////////////////////////////////////
//		int found = 0;
//		
//		for(int i = 0; i < caches.size(); i++){
//			for(int j = 0; j < caches.get(i).cache.length; j++){
//				if(((MemoryEntry)caches.get(i).cache[i][0]).getTag() == tempTag){
//					found = i+1;
//					caches.get(i).cache[i][0] = new MemoryEntry(memoryTagIndex, R[instruction.getOp1()].getData()); // need to be check where do we need to store
//					if((caches.get(i)).hitWritePolicy.equalsIgnoreCase("writethrough"))
//						 memory[instruction.getImmediate() + instruction.getOp2()][0] =  new MemoryEntry(memoryTagIndex, R[instruction.getOp1()].getData());
//					break;
//				}
//			}
//			if(found>0) break;
//		}
//		
//		if(found>0){
//			System.out.println("Found in level " + found + " Cache");
//		}else{
//			for(int i = 0; i < caches.size(); i++){
//				if(caches.get(i).associativity.equalsIgnoreCase("Full Associative"))
//					fullAssociativeLoad(instruction.getImmediate() + instruction.getOp2(), i);
//				else if(caches.get(i).associativity.equalsIgnoreCase("Direct Mapping"))
//					directMappingLoad(instruction.getImmediate() + instruction.getOp2(), i);
//				else if(caches.get(i).associativity.equalsIgnoreCase("Set Associative"))
//					setAssociativeLoad(instruction.getImmediate() + instruction.getOp2(),i);
//			}
//			
//			for(int i = 0; i < caches.size(); i++){
//				for(int j = 0; j < caches.get(i).cache.length; j++){
//					if(((MemoryEntry)caches.get(i).cache[i][0]).getTag() == tempTag){
//						found = i+1;
//						caches.get(i).cache[i][0] = new MemoryEntry(memoryTagIndex, R[instruction.getOp1()].getData()); // need to be check where do we need to store
//						if((caches.get(i)).missWritePolicy.equalsIgnoreCase("writethrough")) // need to check if we need to put it in the cache or not
//							 memory[instruction.getImmediate() + instruction.getOp2()][0] =  new MemoryEntry(memoryTagIndex, R[instruction.getOp1()].getData());
//						break;
//					}
//				}
//				if(found>0) break;
//			}
//		}
		
	}
	
	public void LD(Instruction instruction){
		Object[] temp = memory[instruction.getOp2()];
		int tempTag = ((MemoryEntry)temp[0]).getTag();
		int found = 0;
		
		for(int i = 0; i < caches.size(); i++){
			for(int j = 0; j < caches.get(i).cache.length; j++){
				if(((MemoryEntry)caches.get(i).cache[i][0]).getTag() == tempTag){
					found = i+1;
					R[instruction.getOp1()].setData(((MemoryEntry)caches.get(i).cache[i][instruction.getImmediate()]).getData()); // need to be checked which tag do we need to get from this address
					break;
				}
			}
			if(found>0) break;
		}
		
		if(found>0){
			System.out.println("Found in level " + found + " Cache");
		}else{
			for(int i = 0; i < caches.size(); i++){
				if(caches.get(i).associativity.equalsIgnoreCase("Full Associative"))
					fullAssociativeLoad(instruction.getImmediate() + instruction.getOp2(), i);
				else if(caches.get(i).associativity.equalsIgnoreCase("Direct Mapping"))
					directMappingLoad(instruction.getImmediate() + instruction.getOp2(), i);
				else if(caches.get(i).associativity.equalsIgnoreCase("Set Associative"))
					setAssociativeLoad(instruction.getImmediate() + instruction.getOp2(),i);
			}
		LD(instruction);
		return;
		}
	}
	
	public void fullAssociativeLoad(int address, int cacheIndex){ //which replacement algorithm ??
		Object[] temp= memory[address];
		boolean found = false;
		for(int j = 0; j < (caches.get(cacheIndex)).cache.length;j++){
			if((caches.get(cacheIndex)).cache[j][0] == null){
				if(temp.length <= caches.get(cacheIndex).getL()){
					caches.get(cacheIndex).cache[j] = temp;
				}else if(temp.length > caches.get(cacheIndex).getL()){
					for(int i =0; i < caches.get(cacheIndex).getL(); i++){
						caches.get(cacheIndex).cache[j][i] = temp[i];
					}
				}
				System.out.println("Added to cache address " + j);
				break;
			}
		}
		if(!found){
			int index = address%(caches.get(cacheIndex)).getC();
			if(temp.length <= caches.get(cacheIndex).getL()){
				caches.get(cacheIndex).cache[index] = temp;
			}else if(temp.length > caches.get(cacheIndex).getL()){
				for(int i =0; i < caches.get(cacheIndex).getL(); i++){
					caches.get(cacheIndex).cache[index][i] = temp[i];
				}
			}
		}
	}
	public void directMappingLoad(int address, int cacheIndex){
		Object[] temp= memory[address];
		int index = address%(caches.get(cacheIndex)).getC();
		if(temp.length <= caches.get(cacheIndex).getL()){
			caches.get(cacheIndex).cache[index] = temp;
		}else if(temp.length > caches.get(cacheIndex).getL()){
			for(int i =0; i < caches.get(cacheIndex).getL(); i++){
				caches.get(cacheIndex).cache[index][i] = temp[i];
			}
		}
	}
	
	public void setAssociativeLoad(int address, int cacheIndex){
		Object[] temp= memory[address];
		int index = (address%(caches.get(cacheIndex)).getC()/(caches.get(cacheIndex)).getM())*(caches.get(cacheIndex)).getM();
		if(temp.length <= caches.get(cacheIndex).getL()){
			caches.get(cacheIndex).cache[index] = temp;
		}else if(temp.length > caches.get(cacheIndex).getL()){
			for(int i =0; i < caches.get(cacheIndex).getL(); i++){
				caches.get(cacheIndex).cache[index][i] = temp[i];
			}
		}
	}
	
	public void ADD(Instruction instruction){
		R[instruction.getOp1()]
				.setData((Integer)(R[instruction.getOp2()].getData()) + (Integer)(R[instruction.getOp3()].getData()));
	}
	
	public void ADDI(Instruction instruction){
		R[instruction.getOp1()]
				.setData((Integer)(R[instruction.getOp2()].getData()) + instruction.getImmediate());
	}
	
	public void MUL(Instruction instruction){
		R[instruction.getOp1()]
				.setData((Integer)(R[instruction.getOp2()].getData()) * (Integer)(R[instruction.getOp3()].getData()));
	}
	
	public void DIV(Instruction instruction){
		R[instruction.getOp1()]
				.setData((Integer)(R[instruction.getOp2()].getData()) / (Integer)(R[instruction.getOp3()].getData()));
	}
	
	public void RET(Instruction instruction){
		PC = (Integer)R[instruction.getOp1()].getData();
		branchTaken = 1;
		branchPrediction = false;
	}
	
	public void JALR(Instruction instruction){
		R[instruction.getOp1()].setData(PC+1);
		PC = (Integer)R[instruction.getOp2()].getData();
		branchTaken = 1;
		branchPrediction = false;
	}
	
	public void BEQ(Instruction instruction){
		if(R[instruction.getOp1()].getData().equals(R[instruction.getOp2()].getData())){
			PC = PC + 1 + instruction.getImmediate();
			branchTaken = 1;
		}else
			branchTaken = 0;
		
		branchPrediction = false;
	}
	
	public void JMP(Instruction instruction){
			PC = PC + 1 + (Integer)R[instruction.getOp1()].getData() + instruction.getImmediate();
			branchTaken = 1;
			branchPrediction = false;
	}
	
	public void NAND(Instruction instruction){
		
		String op1 = Integer.toBinaryString((int)R[instruction.getOp2()].getData());
		String op2 = Integer.toBinaryString((int)R[instruction.getOp3()].getData());
		String binaryResult = "";
		int difference = Math.abs(op1.length() - op2.length());
		int i = 0;
		
		for(; i < difference; i++)
			binaryResult += "1";
		
		if(op2.length() > op1.length()){
			
			String temp = op1;
			op1 = op2;
			op2 = temp;
			
		}
		
		for(; i < op1.length(); i++)
			if(!((Integer.parseInt(""+op1.charAt(i))==1) && (Integer.parseInt(""+op2.charAt(i-difference))==1))){
				binaryResult += "1";
			}else
				binaryResult += "0";
		
		
		R[instruction.getOp1()].setData(Integer.parseInt(binaryResult, 2));
		
	}
	
	
	public void decode(Instruction instruction){
		
		System.out.println(instruction.toString() + " is now in Decode");
		
		if(instruction.getType().equalsIgnoreCase("add")
				|| instruction.getType().equalsIgnoreCase("addi")
				|| instruction.getType().equalsIgnoreCase("lw")
				|| instruction.getType().equalsIgnoreCase("sw")
				|| instruction.getType().equalsIgnoreCase("jmp")
				|| instruction.getType().equalsIgnoreCase("beq")
				|| instruction.getType().equalsIgnoreCase("jalr")
				|| instruction.getType().equalsIgnoreCase("ret")
				|| instruction.getType().equalsIgnoreCase("nand")){
			
			int i = 0;
			for(; i < addReservationStation.length; i++)
				if(addReservationStation[i].isFree())
					break;
			if(i < addReservationStation.length){
				
				addReservationStation[i].setFree(false);
				instruction.setReservationStationIndex(i);
				
			}else{
				System.out.println("No reservation stations free.");
				return;
			}
		}
		
		if(instruction.getType().equalsIgnoreCase("mul")){
			
			int i = 0;
			for(; i < multiplyReservationStation.length; i++)
				if(multiplyReservationStation[i].isFree())
					break;
			if(i < multiplyReservationStation.length){
				
				multiplyReservationStation[i].setFree(false);
				instruction.setReservationStationIndex(i);
				
			}else
				return;		
			
		}
		
		if(instruction.getType().equalsIgnoreCase("div")){
			
			int i = 0;
			for(; i < divideReservationStation.length; i++)
				if(divideReservationStation[i].isFree())
					break;
			if(i < divideReservationStation.length){
				
				divideReservationStation[i].setFree(false);
				instruction.setReservationStationIndex(i);
				
			}else
				return;
				
		}
		
		boolean flag = false;
		for(int i = 0; i < robSize; i++)
			if(rob.get(i).isFree()){
				
				rob.get(i).setFree(false);
				instruction.setRobIndex(i);
				flag = true;
				break;
				
			}

		if(!flag){
			System.out.println("No free ROB");
			return;
		}

		instruction.setFinishedStages(1);
		
	}
	
	
	public void dispatch(Instruction instruction){
		
		System.out.println(instruction.toString() + " is now in Dispatch");
		
		int src = instruction.getOp1();
		int op1 = instruction.getOp2();
		int op2 = instruction.getOp3();
		int op1Value = -1, op2Value = -1;
		boolean op1Flag = true, op2Flag = true;
		
		if(op1 != -1){
			
			if(registerMap.get(op1) != null){
				for(int i = rob.size()-1; i>=0; i--){
					if(rob.get(i).getRegister() == op1){
						
						op1Value = rob.get(i).getData();
						op1Flag = rob.get(i).isFlag();
						break;
					}
					
				}
				
			}else
				op1Value = op1;
			
		}
		
		if(op2 != -1){
			
			if(registerMap.get(op2) != null){
				for(int i = rob.size()-1; i>=0; i--){
					if(rob.get(i).getRegister() == op2){
						
						op2Value = rob.get(i).getData();
						op2Flag = rob.get(i).isFlag();
						break;
					}
					
				}
				
			}else
				op2Value = op2;
			
		}
		
		rob.set(instruction.getRobIndex(),new ROBEntry(false, instruction.getRobIndex(), src, false));
		registerMap.put(src, instruction.getRobIndex());
		
		if(instruction.getType().equalsIgnoreCase("add")
				|| instruction.getType().equalsIgnoreCase("addi")
				|| instruction.getType().equalsIgnoreCase("lw")
				|| instruction.getType().equalsIgnoreCase("sw")
				|| instruction.getType().equalsIgnoreCase("jmp")
				|| instruction.getType().equalsIgnoreCase("beq")
				|| instruction.getType().equalsIgnoreCase("jalr")
				|| instruction.getType().equalsIgnoreCase("ret")
				|| instruction.getType().equalsIgnoreCase("nand")){
			
			addReservationStation[instruction.getReservationStationIndex()].setOp1(op1Value);
			addReservationStation[instruction.getReservationStationIndex()].setFlag1(op1Flag);
			addReservationStation[instruction.getReservationStationIndex()].setOp2(op2Value);
			addReservationStation[instruction.getReservationStationIndex()].setFlag2(op2Flag);
			addReservationStation[instruction.getReservationStationIndex()].setTag(instruction.getRobIndex());
			
		}
		
		if(instruction.getType().equalsIgnoreCase("mul")){
			
			multiplyReservationStation[instruction.getReservationStationIndex()].setOp1(op1Value);
			multiplyReservationStation[instruction.getReservationStationIndex()].setFlag1(op1Flag);
			multiplyReservationStation[instruction.getReservationStationIndex()].setOp2(op2Value);
			multiplyReservationStation[instruction.getReservationStationIndex()].setFlag2(op2Flag);
			multiplyReservationStation[instruction.getReservationStationIndex()].setTag(instruction.getRobIndex());
			
		}
		
		if(instruction.getType().equalsIgnoreCase("div")){
			
			divideReservationStation[instruction.getReservationStationIndex()].setOp1(op1Value);
			divideReservationStation[instruction.getReservationStationIndex()].setFlag1(op1Flag);
			divideReservationStation[instruction.getReservationStationIndex()].setOp2(op2Value);
			divideReservationStation[instruction.getReservationStationIndex()].setFlag2(op2Flag);
			divideReservationStation[instruction.getReservationStationIndex()].setTag(instruction.getRobIndex());
		}
		
		instruction.setFinishedStages(2);
		
	}
	
	
	
	public void issue(Instruction instruction){
		
		System.out.println(instruction.toString() + " is now in issue");
		
		boolean op1Ready = false, op2Ready = false;
		
		if (instruction.getType().equalsIgnoreCase("add")
				|| instruction.getType().equalsIgnoreCase("addi")
				|| instruction.getType().equalsIgnoreCase("lw")
				|| instruction.getType().equalsIgnoreCase("sw")
				|| instruction.getType().equalsIgnoreCase("jmp")
				|| instruction.getType().equalsIgnoreCase("beq")
				|| instruction.getType().equalsIgnoreCase("jalr")
				|| instruction.getType().equalsIgnoreCase("ret")
				|| instruction.getType().equalsIgnoreCase("nand")) {
			
			op1Ready = addReservationStation[instruction.getReservationStationIndex()].isFlag1();
			op2Ready = addReservationStation[instruction.getReservationStationIndex()].isFlag2();
			
		}
		
		if(instruction.getType().equalsIgnoreCase("mul")){
			
			op1Ready = multiplyReservationStation[instruction.getReservationStationIndex()].isFlag1();
			op2Ready = multiplyReservationStation[instruction.getReservationStationIndex()].isFlag2();
			
		}
		
		if(instruction.getType().equalsIgnoreCase("div")){
			
			op1Ready = divideReservationStation[instruction.getReservationStationIndex()].isFlag1();
			op2Ready = divideReservationStation[instruction.getReservationStationIndex()].isFlag2();
			
		}
		
		
		if(op1Ready && op2Ready)
			instruction.setFinishedStages(3);
		
	}
	
	
	public void execute(Instruction instruction){
		
		System.out.println(instruction.toString() + " is now in execute");
		
		instruction.setExecutedcycles(instruction.getExecutedcycles() + 1);
		
		if(instruction.getExecutedcycles() == instruction.getExecutionTime()){
			
			//execute the instruction.
			divertToInstructionType(instruction);
			
			//update the ROB.
			for(int i = 0; i < rob.size(); i++)
				if(rob.get(i).getData() == instruction.getRobIndex()){
				
					rob.get(i).setFlag(true);
					rob.get(i).setData(instruction.getOp1());
				
				}
			
			//update the Register Map.
			if(registerMap.get(instruction.getOp1()) == instruction.getRobIndex())
				registerMap.put(instruction.getOp1(), -1);
			
			//Update the Reservation Stations.
			for(int i = 0; i < addReservationStation.length; i++){
				
				if(addReservationStation[i].getOp1() == instruction.getRobIndex()){
					addReservationStation[i].setOp1(instruction.getOp1());
					addReservationStation[i].setFlag1(true);
				}
				
				if(addReservationStation[i].getOp2() == instruction.getRobIndex()){
					addReservationStation[i].setOp2(instruction.getOp1());
					addReservationStation[i].setFlag2(true);
				}
			
			}
			
			for(int i = 0; i < multiplyReservationStation.length; i++){
				
				if(multiplyReservationStation[i].getOp1() == instruction.getRobIndex()){
					multiplyReservationStation[i].setOp1(instruction.getOp1());
					multiplyReservationStation[i].setFlag1(true);
				}
				
				if(multiplyReservationStation[i].getOp2() == instruction.getRobIndex()){
					multiplyReservationStation[i].setOp2(instruction.getOp1());
					multiplyReservationStation[i].setFlag2(true);
				}
			
			}
			
			for(int i = 0; i < divideReservationStation.length; i++){
				
				if(divideReservationStation[i].getOp1() == instruction.getRobIndex()){
					divideReservationStation[i].setOp1(instruction.getOp1());
					divideReservationStation[i].setFlag1(true);
				}
				
				if(divideReservationStation[i].getOp2() == instruction.getRobIndex()){
					divideReservationStation[i].setOp2(instruction.getOp1());
					divideReservationStation[i].setFlag2(true);
				}
			
			}
			
			
			instruction.setFinishedStages(4);
			
		}
		
	}
	
	
	public void commit(Instruction instruction){
		
		System.out.println(instruction.toString() + " is now in commit");
		
		System.out.println("the result is " + R[instruction.getOp1()].getData());
		
		if (instruction.getType().equalsIgnoreCase("add")
				|| instruction.getType().equalsIgnoreCase("addi")
				|| instruction.getType().equalsIgnoreCase("lw")
				|| instruction.getType().equalsIgnoreCase("sw")
				|| instruction.getType().equalsIgnoreCase("jmp")
				|| instruction.getType().equalsIgnoreCase("beq")
				|| instruction.getType().equalsIgnoreCase("jalr")
				|| instruction.getType().equalsIgnoreCase("ret")
				|| instruction.getType().equalsIgnoreCase("nand")) {
			addReservationStation[instruction.getReservationStationIndex()] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		}
		
		if (instruction.getType().equalsIgnoreCase("mul"))
				multiplyReservationStation[instruction.getReservationStationIndex()] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		
		if (instruction.getType().equalsIgnoreCase("div"))
				divideReservationStation[instruction.getReservationStationIndex()] = new Reservation_Station_Entry(-1, -1, -1, true, false, false);
		
		rob.get(instruction.getRobIndex()).setFree(true);
		
		instruction.setFinishedStages(5);
		
	}
	

}
